/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkMediaSource.h"
#include "MkMediaConsumer.h"
#include "MkUtil/MkLog.h"

BOOL CMkMediaSource::m_bCachePacket = TRUE;
CMkMediaSource::CMkMediaSource(const MkMediaSourceType& SourceType)
    : m_ResultCb(nullptr)
    , m_SourceType(SourceType)
    , m_fSpeed(1.0f)
    , m_bPause(FALSE)
    , m_Duration(0)
    , m_FileSize(0)
    , m_SeekPos(0)
    , m_nMaxLen(Len2K)
    , m_nOffset(0)
    , m_RecvBytes(0)
    , m_StartTime(NowMkTime)
    , m_EsPacketCb(nullptr)
{
    m_pBuffer = (Uint8*)malloc(m_nMaxLen);
}

Uint32 CMkMediaSource::StartMediaSource(const MkSourceResultCallback& ResultCb, const MkEsPacketCallback& EsCb/* = nullptr*/)
{
    m_ResultCb = ResultCb;
    m_EsPacketCb = EsCb;
    return NoneError;
}

Uint32 CMkMediaSource::StopMediaSource()
{
    m_ConsumerLock.WLock();
    MkMap<MkMediaConsumeType, CMkMediaConsume*>::iterator it = m_MapMediaConsume.begin();
    for (; it != m_MapMediaConsume.end(); it++) {
        it->second->ClearConsumer();
        MkDelete(it->second);
    }
    m_MapMediaConsume.clear();
    m_ConsumerLock.WUnlock();
    return NoneError;
}

Uint32 CMkMediaSource::PlayControl(const MkPlaybackControlType& ControlType, float fSpeed, Uint32 offsetSec)
{
    switch (ControlType) {
    case MkControlPause:
        m_bPause = TRUE;
        break;
    case MkControlRecovery:
        m_bPause = FALSE;
        break;
    case MkControlSeek:
        m_SeekPos = offsetSec;
        break;
    case MkControlSetSpeed:
        m_fSpeed = fSpeed;
        break;
    default:
        break;
    }
    return NoneError;
}

void CMkMediaSource::AddCacheEsPacket(const MkEsPacket& EsPacket)
{
    if (EsPacket.bKeyPacket) {
        ClearCacheEsPacket();
    }
    MkCacheEsPacket CacheEsPacket;
    CMkMediaUtil::WriteCacheEsPacket(EsPacket, CacheEsPacket);
    m_CacheEsPacket.push_back(CacheEsPacket);
}

Uint32 CMkMediaSource::AddConsumer(CMkMediaConsume* pConsume, CMkMediaConsumer* pConsumer)
{
    m_ConsumerLock.WLock();
    MkMap<MkMediaConsumeType, CMkMediaConsume*>::iterator it = m_MapMediaConsume.find(pConsumer->GetConsumeType());
    if (it != m_MapMediaConsume.end()) {
        it->second->AddConsumer(pConsumer);
        MkDelete(pConsume);
    } else {
        pConsume->AddConsumer(pConsumer);
        m_MapMediaConsume[pConsumer->GetConsumeType()] = pConsume;
    }
    m_ConsumerLock.WUnlock();
    return NoneError;
}

Uint32 CMkMediaSource::OnReadEsPacket(const MkEsPacket& EsPacket)
{
    m_RecvBytes += EsPacket.BufferList.GetTatolLen();
    //media source end
    if (EsPacket.BufferList.Empty()) 
    {
        ClearConsume();
    } else {
        m_ConsumerLock.RLock();
        MkMap<MkMediaConsumeType, CMkMediaConsume*>::iterator it = m_MapMediaConsume.begin();
        for (; it != m_MapMediaConsume.end(); it++) {
            it->second->ConsumeEsPacket(this, EsPacket, m_pBuffer, m_nMaxLen);
        }
        m_ConsumerLock.RUnlock();
        if (m_bCachePacket) {
            AddCacheEsPacket(EsPacket);
        }
        if (m_EsPacketCb) {
            m_EsPacketCb(EsPacket, m_VideoParam, m_AudioParam);
        }
    }
    return NoneError;
}

Uint32 CMkMediaSource::DeleteConsumer(const MkMediaConsumeType& ConsumeType, const MkString& ConsumerSession, BOOL& bEmpty)
{
    Uint32 ErrorCode = InvalidSession;
    m_ConsumerLock.WLock();
    MkMap<MkMediaConsumeType, CMkMediaConsume*>::iterator it = m_MapMediaConsume.find(ConsumeType);
    if (it != m_MapMediaConsume.end()) {
        ErrorCode = it->second->DeleteConsumer(ConsumerSession);
        if (it->second->IsEmpty()) {
            MkDelete(it->second);
            m_MapMediaConsume.erase(it);
        }
    }
    bEmpty = m_MapMediaConsume.empty();
    m_ConsumerLock.WUnlock();
    return ErrorCode;
}

Uint32 CMkMediaSource::GetConsumer(const MkMediaConsumeType& ConsumeType, const MkString& ConsumerSession, CMkMediaConsumer** ppConsumer)
{
    Uint32 ErrorCode = InvalidSession;
    m_ConsumerLock.WLock();
    MkMap<MkMediaConsumeType, CMkMediaConsume*>::iterator it = m_MapMediaConsume.find(ConsumeType);
    if (it != m_MapMediaConsume.end()) {
        ErrorCode = it->second->GetConsumer(ConsumerSession, ppConsumer);
    }
    m_ConsumerLock.WUnlock();
    return ErrorCode;
}

void CMkMediaSource::ClearCacheEsPacket()
{
    MkVector<MkCacheEsPacket>::iterator it = m_CacheEsPacket.begin();
    for (; it != m_CacheEsPacket.end(); it++) {
        it->CacheBufferList.Clear();
    }
    m_CacheEsPacket.clear();
}

void CMkMediaSource::ClearConsume()
{
    MkMap<MkMediaConsumeType, CMkMediaConsume*> TmpMapMediaConsume;
    m_ConsumerLock.WLock();
    TmpMapMediaConsume = m_MapMediaConsume;
    m_MapMediaConsume.clear();
    m_ConsumerLock.WUnlock();
    MkMap<MkMediaConsumeType, CMkMediaConsume*>::iterator it = TmpMapMediaConsume.begin();
    for (; it != TmpMapMediaConsume.end();it++) {
        it->second->ClearConsumer();
        MkDelete(it->second);
    }
    TmpMapMediaConsume.clear();
}

CMkMediaSource::~CMkMediaSource()
{
    ClearCacheEsPacket();
    MkFree(m_pBuffer);
}